﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using HynuIOTA.Entity;
using HynuIOTA.Entity.FormatEntity;
using System.Reflection;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;

namespace HynuIOTA.Respository
{
    /// <summary>
    /// 仓储模式公共基类
    /// </summary>
    public class RepositoryBase<T> : IRespository.IRespositoryBase<T> where T : class, new()
    {
        protected HynuIOTAEntitiesContainer DbContext => EFFactory.GetDBContext();

        #region 1.0 新增方法
        /// <summary>
        /// 新增方法
        /// </summary>
        /// <param name="model">模型</param>
        /// <returns></returns>
        public void Add(T model)
        {
            DbContext.Set<T>().Add(model);
        }
        #endregion

        #region 2.0 删除方法
        /// <summary>
        /// 删除方法
        /// </summary>
        /// <param name="model">模型</param>
        /// <returns></returns>
        public void Delete(T model)
        {
            DbContext.Entry<T>(model).State = System.Data.Entity.EntityState.Deleted;
        }

        /// <summary>
        /// 根据条件删除
        /// </summary>
        /// <param name="where">删除的条件</param>
        /// <returns></returns>
        public void DeleteBy(Expression<Func<T, bool>> whereLambda)
        {
            var models = DbContext.Set<T>().Where(whereLambda);
            DbContext.Set<T>().RemoveRange(models);
        }
        #endregion

        #region 3.0 修改方法
        /// <summary>
        /// 修改实体
        /// </summary>
        /// <param name="model">模型</param>
        /// <returns></returns>
        public void Modify(T model)
        {
            DbContext.Entry<T>(model).State = EntityState.Modified;
        }

        /// <summary>
        /// 修改实体
        /// </summary>
        /// <param name="model">模型</param>
        /// <param name="modifiedPropertyNames">修改的属性</param>
        /// <returns></returns>
        public void Modify(T model, params string[] modifiedPropertyNames)
        {
            var entry = DbContext.Entry<T>(model);
            entry.State = System.Data.Entity.EntityState.Unchanged;
            foreach (string pName in modifiedPropertyNames)
            {
                entry.Property(pName).IsModified = true;
            }
        }

        /// <summary>
        /// 通过从给定对象中读取值来设置此字典的值。给定对象可以是任何类型。如果对象具有任何与字典中的属性名称匹配且可读取的名称的属性，则将读取这些属性。其他属性将被忽略。例如，这样便能从简单的数据传输对象(DTO) 中复制属性。
        /// </summary>
        /// <param name="model"></param>
        /// <param name="obj"></param>
        public void ModifyValue(T model, object obj)
        {
            var entry = DbContext.Entry<T>(model);
            entry.CurrentValues.SetValues(obj);

        }

        /// <summary>
        /// 修改指定实体
        /// </summary>
        /// <param name="whereLamdba">修改条件</param>
        /// <param name="modifyPropertyNamesValues">修改属性键值对</param>
        /// <returns></returns>
        public void ModifyBy(Expression<Func<T, bool>> whereLamdba, Dictionary<string, object> modifyPropertyNamesValues)
        {
            var models = DbContext.Set<T>().Where(whereLamdba);

            Type t = typeof(T);

            foreach (var model in models)
            {
                foreach (var pNameValue in modifyPropertyNamesValues)
                {
                    PropertyInfo pi = t.GetProperty(pNameValue.Key);
                    pi.SetValue(model, pNameValue.Value);
                }
            }
        }
        #endregion

        #region 4.0 查询方法
        /// <summary>
        /// 查询返回的第一个或默认
        /// </summary>
        /// <param name="whereLamdba">查询条件</param>
        /// <returns></returns>
        public T WhereFirstOrDefault(Expression<Func<T, bool>> whereLamdba)
        {
            return Where(whereLamdba).FirstOrDefault();
        }

        /// <summary>
        /// 根据指定条件查询 - 延时
        /// </summary>
        /// <param name="whereLamdba">查询条件</param>
        /// <returns></returns>
        public IQueryable<T> Where(Expression<Func<T, bool>> whereLamdba)
        {
            return DbContext.Set<T>().Where(whereLamdba);
        }

        /// <summary>
        /// 根据指定条件进行连接查询 - 延时
        /// </summary>
        /// <param name="whereLamdba">查询条件</param>
        /// <param name="foreignKeyPropertyNames">外键属性名</param>
        /// <returns></returns>
        public IQueryable<T> Where(Expression<Func<T, bool>> whereLamdba, params string[] foreignKeyPropertyNames)
        {
            DbQuery<T> dbQuery = DbContext.Set<T>();

            if (foreignKeyPropertyNames.Length > 0)
            {
                for (int i = 0; i < foreignKeyPropertyNames.Length; i++)
                {
                    dbQuery = dbQuery.Include(foreignKeyPropertyNames[i]);
                }
            }

            return dbQuery.Where(whereLamdba);
        }

        /// <summary>
        /// 根据指定条件查询并排序 - 延时
        /// </summary>
        /// <typeparam name="TKey">排序字段类型</typeparam>
        /// <param name="whereLamdba">查询条件</param>
        /// <param name="orderBy">排序字段</param>
        /// <param name="isAsc">是否升序</param>
        /// <returns></returns>
        public IQueryable<T> WhereOrderBy<TKey>(Expression<Func<T, bool>> whereLamdba, Expression<Func<T, TKey>> orderBy, bool isAsc = true)
        {
            return isAsc ? DbContext.Set<T>().Where(whereLamdba).OrderBy(orderBy) : DbContext.Set<T>().Where(whereLamdba).OrderBy(orderBy);
        }

        /// <summary>
        /// /根据指定条件进行连接查询、排序 - 延时
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="whereLamdba">查询条件</param>
        /// <param name="orderBy">排序字段</param>
        /// <param name="isAsc">是否升序</param>
        /// <param name="foreignKeyPropertyNames">连接查询外键名称</param>
        /// <returns></returns>
        public IQueryable<T> WhereOrderBy<TKey>(Expression<Func<T, bool>> whereLamdba, Expression<Func<T, TKey>> orderBy, bool isAsc = true, params string[] foreignKeyPropertyNames)
        {
            DbQuery<T> dbQuery = DbContext.Set<T>();

            foreach (var pName in foreignKeyPropertyNames)
            {
                dbQuery = dbQuery.Include(pName);
            }

            return isAsc ? dbQuery.Where(whereLamdba).OrderBy(orderBy) : dbQuery.Where(whereLamdba).OrderBy(orderBy);
        }


        /// <summary>
        /// 根据指定条件进行分页查询、排序 - 延时
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="pageData"></param>
        /// <param name="whereLamdba"></param>
        /// <param name="orderBy"></param>
        /// <param name="isAsc"></param>
        /// <returns></returns>
        public IQueryable<T> WherePage<TKey>(Entity.FormatEntity.PageData pageData, Expression<Func<T, bool>> whereLamdba,
            Expression<Func<T, TKey>> orderBy, bool isAsc = true)
        {
            var models = isAsc ? DbContext.Set<T>().Where(whereLamdba).OrderBy(orderBy) : DbContext.Set<T>().Where(whereLamdba).OrderByDescending(orderBy);

            pageData.RowCount = models.Count();

            return models.Skip((pageData.PageIndex - 1) * pageData.PageSize).Take(pageData.PageSize);
        }

        /// <summary>
        /// 根据指定条件进行连接分页查询、排序 - 延时
        /// </summary>
        /// <typeparam name="TKey">排序字段类型</typeparam>
        /// <param name="pageData">页面数据</param>
        /// <param name="whereLamdba">查询条件</param>
        /// <param name="orderBy">排序字段</param>
        /// <param name="isAsc">是否升序</param>
        /// <param name="foreignKeyPropertyNames">连接查询外键名称</param>
        /// <returns></returns>
        public IQueryable<T> WherePage<TKey>(Entity.FormatEntity.PageData pageData, Expression<Func<T, bool>> whereLamdba,
            Expression<Func<T, TKey>> orderBy, bool isAsc = true, params string[] foreignKeyPropertyNames)
        {
            DbQuery<T> dbQuery = DbContext.Set<T>();
            foreach (var pName in foreignKeyPropertyNames)
            {
                dbQuery = dbQuery.Include(pName);
            }

            var models = isAsc ? dbQuery.Where(whereLamdba).OrderBy(orderBy) : dbQuery.Where(whereLamdba).OrderByDescending(orderBy);

            pageData.RowCount = models.Count();

            return models.Skip((pageData.PageIndex - 1) * pageData.PageSize).Take(pageData.PageSize);
        }

        public async Task<IQueryable<T>> WherePageAsync<TKey>(PageData pageData, Expression<Func<T, bool>> whereLamdba, Expression<Func<T, TKey>> orderBy, bool isAsc = true)
        {
            var models = isAsc ? DbContext.Set<T>().Where(whereLamdba).OrderBy(orderBy) : DbContext.Set<T>().Where(whereLamdba).OrderByDescending(orderBy);

            pageData.RowCount = await models.CountAsync();

            return models.Skip((pageData.PageIndex - 1) * pageData.PageSize).Take(pageData.PageSize);
        }

        public async Task<IQueryable<T>> WherePageAsync<TKey>(PageData pageData, Expression<Func<T, bool>> whereLamdba, Expression<Func<T, TKey>> orderBy, bool isAsc = true, params string[] foreignKeyPropertyNames)
        {
            DbQuery<T> dbQuery = DbContext.Set<T>();
            foreach (var pName in foreignKeyPropertyNames)
            {
                dbQuery = dbQuery.Include(pName);
            }

            var models = isAsc ? dbQuery.Where(whereLamdba).OrderBy(orderBy) : dbQuery.Where(whereLamdba).OrderByDescending(orderBy);

            pageData.RowCount = await models.CountAsync();

            return models.Skip((pageData.PageIndex - 1) * pageData.PageSize).Take(pageData.PageSize);
        }

        #endregion
    }
}
